/**
* @file statistics.c
* @author Jan Sliacky
* 
* Save some useful functions for statistics
*
* April 2010
*
* Copyright (c) 2009 by Jan Sliacky
* All rights reserved.
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include "stats.h"
#include "tpl.h"
#include "globals.h"

typedef struct TasksStruct {
    int	taskID;
    void*	next;
}
TasksContainer;

int nFalls = 0;							/* number of falls										*/
int nInterMessages = 0;					/* number of interrupted tasks							*/
int nSends = 0;							/* number of sends										*/
int nOFFSends = 0;						/* number of sends MSG_OFF message						*/
int nBC = 0;							/* number of broadcasts									*/
int nNeedWorkBC = 0;					/* number of NEED_WORK broadcasts						*/
int nWantUnassignBC = 0;				/* number of WANT_UNASSIGN broadcasts					*/
int nMySolvedTasks = 0;					/* number of my own solved tasks						*/
int nTests = 0;							/* number of tests to fall								*/
float expectedTime = 0;					/* the best possible computing time						*/
float wasteTime = 0;					/* time spending to solve the interrupted tasks			*/
float solveTasksTime = 0;				/* time spending on successfully solved tasks			*/
float waitForNewTasksTime = 0;			/* time spending on waiting for new tasks				*/
float fallTime = 0;						/* amount of the time when processor is fallen			*/
float lastFallTime = 0;					/* amount of the time last fallen						*/
float totalTime = 0;					/* total time of the compute							*/
TasksContainer* tasksCont;				/* record of tasks to do								*/

static int nFallsGlobal;				/* number of falls 										*/
static int nInterMessagesGlobal;		/* number of interrupted tasks 							*/
static int nSendsGlobal;				/* number of sends 										*/
static int nOFFSendsGlobal;				/* number of sends MSG_OFF messages 					*/
static int nBCGlobal;					/* number of broadcasts 								*/
static int nNeedWorkBCGlobal;			/* number of NEED_WORK broadcasts 						*/
static int nWantUnassignBCGlobal;		/* number of WANT_UNASSIGN broadcasts 					*/
static int nSolvedTasks;				/* number of tasks solved by processor 					*/
static int nDuplicateTasks;				/* number of tasks computed duplicates 					*/
static float expectedTimeGlobal;		/* the best possible computing time						*/
static float wasteTimeGlobal;			/* amount of the time spending to solve the interrupted tasks */
static float waitForNewTasksTimeGlobal;	/* amount of the time spending on waiting for new tasks	*/
static float fallTimeGlobal;			/* amount of the time when processor is fallen			*/
static float totalTimeGlobal;			/* amount of the time spending on computing 			*/
static float bestTime = 100000;			/* best total time of the compute 						*/
static float notWasteTimeGlobal;		/* amount of the time spending to successfully solved tasks */
static int allTasks[MAXTASKCNT];		/* array of all tasks									 */

struct timeval startTotalTime;
struct timeval startSolveTasksTime;
struct timeval startWaitTime;
struct timeval startWasteTime;
struct timeval startFallTime;
struct timeval startTimerTime;

/**
 * Compute the difference between time t1 and time t2
 * @author Jan Sliacky
 * @return time
 */
float computeTime(struct timeval t1, struct timeval t2) {
    return(t2.tv_sec - t1.tv_sec + (t2.tv_usec - t1.tv_usec) / 1e6);
}

/**
 * Compute the difference between time t1 and time t2
 * @author Jan Sliacky
 * @return time
 */
float computeTimeWithActual(struct timeval t1) {
    struct timeval t2;
    gettimeofday(&t2, NULL);
    return(t1.tv_sec - t2.tv_sec + (t1.tv_usec - t2.tv_usec) / 1e6);
}

/**
 * Add time addPart to actual time
 * @author Jan Sliacky
 * @return actual time + addPart
 */
struct timeval addTimeToActual(float addPart) {
    struct timeval t1;
    int addsec, addusec, modulo;

    addsec = (int)addPart;
    addusec = (int)((addPart - (float)addsec) * 1000000);
    gettimeofday(&t1, NULL);
    modulo = (int) ((t1.tv_usec + addusec) / 1000000);
    t1.tv_sec = t1.tv_sec + addsec + modulo;
    t1.tv_usec = t1.tv_usec + addusec - (modulo * 1000000);
    return(t1);
}

/**
 * Start timer
 * @author Jan Sliacky
 */
void startTimer(void) {
    gettimeofday(&startTimerTime, NULL);
}

/**
 * Get difference between startTimerTime and actual time
 * @author Jan Sliacky
 * @return time
 */
float getTimer(void) {
    struct timeval tvpom;

    gettimeofday(&tvpom, NULL);
    return(computeTime(startTimerTime, tvpom));
}

/**
 * Increment falls counter
 * @author Jan Sliacky
 */
void incNFalls(void) {
    nFalls += 1;
}

/**
 * Increment falls counter
 * @author Jan Sliacky
 */
void incNOFFSends(void) {
    nOFFSends += 1;
}

/**
 * Increment interrupt tasks counter
 * @author Jan Sliacky
 */
void incNInterMessages(void) {
    nInterMessages += 1;
}

/**
 * Increment sends counter
 * @author Jan Sliacky
 */
void incNSends(void) {
    nSends += 1;
}

/**
 * Increment brodcasts counter
 * @author Jan Sliacky
 */
void incNBC(void) {
    nBC += 1;
}

/**
 * Increment "need work" brodcasts counter
 * @author Jan Sliacky
 */
void incNNeedWorkBC(void) {
    nNeedWorkBC += 1;
}

/**
 * Increment "want unassign" brodcasts counter
 * @author Jan Sliacky
 */
void incNWantUnassignBC(void) {
    nWantUnassignBC += 1;
}

/**
 * Add task complexity to sum of all tasks complexicities
 * @author Jan Sliacky
 */
void addToExpectedTime(float tValue) {
    expectedTimeGlobal += tValue;
}

/**
 * Add my solved task to array
 * @author Jan Sliacky
 * @param	tID	ID of solved task
 */
void addMySolvedTask(int tID, float tValue) {
    TasksContainer* TCpom;

    if (tasksCont == NULL) {
        tasksCont = (TasksContainer *) malloc(sizeof(TasksContainer));
        tasksCont->taskID = tID;
        tasksCont->next = NULL;
    } else {
        TCpom = (TasksContainer *) malloc(sizeof(TasksContainer));
        TCpom->taskID = tID;
        TCpom->next = tasksCont;
        tasksCont = TCpom;
    }
    expectedTime += tValue;
    nMySolvedTasks += 1;
}

/**
 * Take id from last solved task in the array
 * @author Jan Sliacky
 * @return	tID	ID of solved task
 */
int getFromSolvedTasks(void) {
    TasksContainer* TCpom;
    int ipom;

    if (nMySolvedTasks > 0) {
        ipom = tasksCont->taskID;
        TCpom = tasksCont;
        tasksCont = tasksCont->next;
        free(TCpom);
        return(ipom);
    } else {
        return(-1);
    }
}

/**
 * Set start time
 * @author Jan Sliacky
 */
void setStartTotalTime(void) {
    gettimeofday(&startTotalTime, NULL);
}

/**
 * Set end time
 * @author Jan Sliacky
 */
void setEndTotalTime(void) {
    struct timeval tvpom;

    gettimeofday(&tvpom, NULL);
    totalTime += computeTime(startTotalTime, tvpom);
}

/**
 * Start measure of the computing solved task
 * @author Jan Sliacky
 */
void setStartSolveTasksTime(void) {
    gettimeofday(&startSolveTasksTime, NULL);
}

/**
 * Stop measure of the computing solved task
 * @author Jan Sliacky
 */
void setEndSolveTasksTime(void) {
    struct timeval tvpom;

    gettimeofday(&tvpom, NULL);
    solveTasksTime += computeTime(startSolveTasksTime, tvpom);
}

/**
 * Start measure of the wasting computing
 * @author Jan Sliacky
 */
void setStartWasteTime(void) {
    gettimeofday(&startWasteTime, NULL);
}

/**
 * Stop measure of the wasting computing
 * @author Jan Sliacky
 */
void setEndWasteTime(void) {
    struct timeval tvpom;

    gettimeofday(&tvpom, NULL);
    wasteTime += computeTime(startWasteTime, tvpom);
    nInterMessages++;
}

/**
 * Start measure time of the fall
 * @author Jan Sliacky
 */
void setStartWaitTime(void) {
    gettimeofday(&startWaitTime, NULL);
}

/**
 * Stop measure time of the fall
 * @author Jan Sliacky
 */
void setEndWaitTime(void) {
    struct timeval tvpom;

    gettimeofday(&tvpom, NULL);
    waitForNewTasksTime += computeTime(startWaitTime, tvpom);
}

/**
 * Start measure time of the fall
 * @author Jan Sliacky
 */
void setStartFallTime(void) {
    gettimeofday(&startFallTime, NULL);
}

/**
 * Stop measure time of the fall
 * @author Jan Sliacky
 */
void setEndFallTime(void) {
    struct timeval tvpom;

    gettimeofday(&tvpom, NULL);
    lastFallTime = computeTime(startFallTime, tvpom);
    fallTime += lastFallTime;
}

/**
 * Read stat file of one processor
 * @author Jan Sliacky
 * @param	ifile	file name
 */
int readStatFile(char *ifile) {
    FILE* file;
    int i, ipom = 0;
    float fpom1 = 0;

    /* open source file */
    file = fopen(ifile, "r");
    if (file == NULL) {
        printf("input file has probably wrong path\n\n");
        return(1);
    }
    fscanf(file,"NR_FALLS=%d\n", &ipom);
    nFallsGlobal += ipom;
    fscanf(file,"NR_INTER_MESSAGES=%d\n", &ipom);
    nInterMessagesGlobal += ipom;
    fscanf(file,"NR_SENDS=%d\n", &ipom);
    nSendsGlobal += ipom;
    fscanf(file,"NR_OFF_SENDS=%d\n", &ipom);
    nOFFSendsGlobal += ipom;
    fscanf(file,"NR_BC=%d\n", &ipom);
    nBCGlobal += ipom;
    fscanf(file,"NR_NEED_WORK_BC=%d\n", &ipom);
    nNeedWorkBCGlobal += ipom;
    fscanf(file,"NR_WANT_UNASSIGN_BC=%d\n", &ipom);
    nWantUnassignBCGlobal += ipom;
    fscanf(file,"NR_SOLVED_TASKS=%d\n", &ipom);
    nSolvedTasks = ipom;
    fscanf(file,"EXPECTED_TIME=%f\n", &fpom1);
    fscanf(file,"WASTE_TIME=%f\n", &fpom1);
    wasteTimeGlobal += fpom1;
    fscanf(file,"WAIT4TASKS_TIME=%f\n", &fpom1);
    waitForNewTasksTimeGlobal += fpom1;
    fscanf(file,"FALL_TIME=%f\n", &fpom1);
    fallTimeGlobal += fpom1;
    fscanf(file,"TOTAL_TIME=%f\n", &fpom1);
    totalTimeGlobal += fpom1;
    if (fpom1 < bestTime) {
        bestTime = fpom1;
    }
    fscanf(file,"NOT_WASTE_TIME=%f\n", &fpom1);
    notWasteTimeGlobal += fpom1;
    fscanf(file,"SOLVED_TASKS=");
    for (i = 0; i < nSolvedTasks; i++) {
        fscanf(file," %d", &ipom);
        if (allTasks[ipom] == 1) {
            nDuplicateTasks += 1;
        } else {
            allTasks[ipom] = 1;
        }
    }
    printf("\n");
    fclose(file);
    return(0);
}

/**
 * Make statistics file of the process
 * @author Jan Sliacky
 * @param	name			name of the file with statistics
 * @param	proc_rank	rank of the processor
 * @return 0 if success, 1 if not
 */
int saveLocalStatistics(char *name, int proc_rank) {
    FILE* file = NULL;
    char filename[100];
    int i;

    sprintf(filename, "%s_%d", name, proc_rank);
    file = fopen(filename, "w");
    if (file == NULL) {
        printf("Error in creating ststistics file %s\n", filename);
        return(EXIT_FAILURE);
    }

    fprintf(file,"NR_FALLS=%d\n", nFalls);
    fprintf(file,"NR_INTER_MESSAGES=%d\n", nInterMessages);
    fprintf(file,"NR_SENDS=%d\n", nSends);
    fprintf(file,"NR_OFF_SENDS=%d\n", nOFFSends);
    fprintf(file,"NR_BC=%d\n", nBC);
    fprintf(file,"NR_NEED_WORK_BC=%d\n", nNeedWorkBC);
    fprintf(file,"NR_WANT_UNASSIGN_BC=%d\n", nWantUnassignBC);
    fprintf(file,"NR_SOLVED_TASKS=%d\n", nMySolvedTasks);
    fprintf(file,"EXPECTED_TIME=%.2f\n", expectedTime);
    fprintf(file,"WASTE_TIME=%.2f\n", wasteTime);
    fprintf(file,"WAIT4TASKS_TIME=%.2f\n", waitForNewTasksTime);
    fprintf(file,"FALL_TIME=%.2f\n", fallTime);
    fprintf(file,"TOTAL_TIME=%.2f\n", totalTime);
    fprintf(file,"NOT_WASTE_TIME=%.2f\n", solveTasksTime);
    fprintf(file,"SOLVED_TASKS=");
    for(i = 0; i < nMySolvedTasks; i++) {
        fprintf(file," %d", getFromSolvedTasks());
    }
    fprintf(file,"\n");

    fclose(file);
    return(EXIT_SUCCESS);
}

/**
 * Make common statistics for all processors
 * @author Jan Sliacky
 * @param	inputfilename	name of the input configuration file name
 * @param	proc_count		count of processors
 * @return 0 if success, 1 if not
 */
int saveGlobalStatistics(char *inputfilename, int proc_count) {
    char filename[20];
    FILE* file = NULL;
    int i;

    for (i = 0; i < proc_count; i++) {
        sprintf(filename, "%s_%d", inputfilename, i);
        readStatFile(filename);
    }

    file = fopen(inputfilename, "a");
    if (file == NULL) {
        printf("Error in opening ststistics file %s\n", filename);
        return(EXIT_FAILURE);
    }

    fprintf(file,"\n");

    fprintf(file,"FALLS=%d\n", nFallsGlobal);
    fprintf(file,"INTER_MESSAGES=%d\n", nInterMessagesGlobal);
    fprintf(file,"DUPLICATES=%d\n", nDuplicateTasks);
    fprintf(file,"SENDS=%d\n", nSendsGlobal);
    fprintf(file,"OFF_SEND=%d\n", nOFFSendsGlobal);
    fprintf(file,"BC=%d\n", nBCGlobal);
    fprintf(file,"NEED_WORK_BC=%d\n", nNeedWorkBCGlobal);
    fprintf(file,"UNASSIGN_BC=%d\n", nWantUnassignBCGlobal);
    fprintf(file,"EXPECTED_TIME=%.2f\n", expectedTimeGlobal);
    fprintf(file,"WASTE_TIME=%.2f\n", wasteTimeGlobal);
    fprintf(file,"WAIT4TASKS_TIME=%.2f\n", waitForNewTasksTimeGlobal);
    fprintf(file,"COMPUTE_TIME=%.2f\n", notWasteTimeGlobal);
    fprintf(file,"FALL_TIME=%.2f\n", fallTimeGlobal);
    fprintf(file,"TOTAL_TIME=%.2f\n", totalTimeGlobal);
    fprintf(file,"REAL_TIME=%.2f\n", bestTime);

    fclose(file);
    return(EXIT_SUCCESS);
}
